﻿using System;
using System.Collections.Generic;

namespace OpenCvSharp
{
    static partial class Cv2
    {
        /// <summary>
        /// Groups the object candidate rectangles.
        /// </summary>
        /// <param name="rectList"> Input/output vector of rectangles. Output vector includes retained and grouped rectangles.</param>
        /// <param name="groupThreshold">Minimum possible number of rectangles minus 1. The threshold is used in a group of rectangles to retain it.</param>
        /// <param name="eps"></param>
        public static void GroupRectangles(IList<Rect> rectList, int groupThreshold, double eps = 0.2)
        {
            if (rectList == null)
                throw new ArgumentNullException(nameof(rectList));

            using var rectListVec = new VectorOfRect(rectList);

            NativeMethods.HandleException(
                NativeMethods.objdetect_groupRectangles1(rectListVec.CvPtr, groupThreshold, eps));

            ClearAndAddRange(rectList, rectListVec.ToArray());
        }

        /// <summary>
        /// Groups the object candidate rectangles.
        /// </summary>
        /// <param name="rectList"> Input/output vector of rectangles. Output vector includes retained and grouped rectangles.</param>
        /// <param name="weights"></param>
        /// <param name="groupThreshold">Minimum possible number of rectangles minus 1. The threshold is used in a group of rectangles to retain it.</param>
        /// <param name="eps">Relative difference between sides of the rectangles to merge them into a group.</param>
        public static void GroupRectangles(IList<Rect> rectList, out int[] weights, int groupThreshold, double eps = 0.2)
        {
            if (rectList == null)
                throw new ArgumentNullException(nameof(rectList));

            using var rectListVec = new VectorOfRect(rectList);
            using var weightsVec = new VectorOfInt32();

            NativeMethods.HandleException(
                NativeMethods.objdetect_groupRectangles2(rectListVec.CvPtr, weightsVec.CvPtr, groupThreshold, eps));

            ClearAndAddRange(rectList, rectListVec.ToArray());
            weights = weightsVec.ToArray();
        }

        /// <summary>
        /// Groups the object candidate rectangles.
        /// </summary>
        /// <param name="rectList"></param>
        /// <param name="groupThreshold"></param>
        /// <param name="eps"></param>
        /// <param name="weights"></param>
        /// <param name="levelWeights"></param>
        public static void GroupRectangles(IList<Rect> rectList, int groupThreshold, double eps, out int[] weights, out double[] levelWeights)
        {
            if (rectList == null)
                throw new ArgumentNullException(nameof(rectList));

            using var rectListVec = new VectorOfRect(rectList);
            using var weightsVec = new VectorOfInt32();
            using var levelWeightsVec = new VectorOfDouble();

            NativeMethods.HandleException(
                NativeMethods.objdetect_groupRectangles3(
                    rectListVec.CvPtr, groupThreshold, eps, weightsVec.CvPtr, levelWeightsVec.CvPtr));

            ClearAndAddRange(rectList, rectListVec.ToArray());
            weights = weightsVec.ToArray();
            levelWeights = levelWeightsVec.ToArray();
        }

        /// <summary>
        /// Groups the object candidate rectangles.
        /// </summary>
        /// <param name="rectList"></param>
        /// <param name="rejectLevels"></param>
        /// <param name="levelWeights"></param>
        /// <param name="groupThreshold"></param>
        /// <param name="eps"></param>
        public static void GroupRectangles(IList<Rect> rectList, out int[] rejectLevels, out double[] levelWeights, int groupThreshold, double eps = 0.2)
        {
            if (rectList == null)
                throw new ArgumentNullException(nameof(rectList));

            using var rectListVec = new VectorOfRect(rectList);
            using var rejectLevelsVec = new VectorOfInt32();
            using var levelWeightsVec = new VectorOfDouble();

            NativeMethods.HandleException(
                NativeMethods.objdetect_groupRectangles4(
                    rectListVec.CvPtr, rejectLevelsVec.CvPtr, levelWeightsVec.CvPtr, groupThreshold, eps));

            ClearAndAddRange(rectList, rectListVec.ToArray());
            rejectLevels = rejectLevelsVec.ToArray();
            levelWeights = levelWeightsVec.ToArray();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="rectList"></param>
        /// <param name="foundWeights"></param>
        /// <param name="foundScales"></param>
        /// <param name="detectThreshold"></param>
        /// <param name="winDetSize"></param>
        public static void GroupRectanglesMeanshift(IList<Rect> rectList, out double[] foundWeights,
            out double[] foundScales, double detectThreshold = 0.0, Size? winDetSize = null)
        {
            if (rectList == null)
                throw new ArgumentNullException(nameof(rectList));

            var winDetSize0 = winDetSize.GetValueOrDefault(new Size(64, 128));

            using var rectListVec = new VectorOfRect(rectList);
            using var foundWeightsVec = new VectorOfDouble();
            using var foundScalesVec = new VectorOfDouble();

            NativeMethods.HandleException(
                NativeMethods.objdetect_groupRectangles_meanshift(
                    rectListVec.CvPtr, foundWeightsVec.CvPtr, foundScalesVec.CvPtr, detectThreshold, winDetSize0));

            ClearAndAddRange(rectList, rectListVec.ToArray());
            foundWeights = foundWeightsVec.ToArray();
            foundScales = foundScalesVec.ToArray();
        }

        private static void ClearAndAddRange<T>(ICollection<T> list, IEnumerable<T> values)
        {
            list.Clear();
            foreach (var t in values)
            {
                list.Add(t);
            }
        }
    }
}
